<template>
  <div>
    <!-- <p>{{ sentence }}</p> -->
    <h1 ref="myRef">{{ count }}</h1>
    <h1>{{ name }}</h1>
  </div>
</template>

<script>
import {
  ref,
  reactive,
  computed,
  watchEffect,
  watch,
  onMounted,
  onBeforeUpdate
} from 'vue';
import Test from '@/components/Test';

export default {
  name: 'App',
  components: {
    Test
  },
  // watch() {},
  setup(props, ctx) { // 入口函数，

    // computed
    // 也可以使用具有 get 和 set 函数的对象来创建可写的 ref 对象

    // const phrase = ref('欢迎来到');

    // const sentence = computed(() => 'Charlie, ' + phrase.value)

    // or

    // 也可以使用具有 get 和 set 函数的对象来创建可写的 ref 对象

    // 内部是proxy实现的
    // const sentence = computed({
    //   get: () => {
    //     return 'Charlie, ' + phrase.value;
    //   },
    //   set: newVal => {
    //     console.log('我修改了sentence的值', newVal);
    //     phrase.value + newVal;
    //   }
    // })

    // sentence.value += '这个学习的乐园';

    // console.log(sentence.value);

    // return {
    //   sentence
    // }

    // --------------------------------------------------

    // watchEffect
    // 在响应式地跟踪其依赖项时立即运行一个函数，并在更改依赖项时重新运行它

    // const count = ref(0),
    //       myRef = ref(null);

    // setTimeout(() => {
    //   count.value = 1;
    // }, 1000);

    // setTimeout(() => {
    //   stop();
    //   console.log('watchEffect is stopped');
    // }, 2000);

    // setTimeout(() => {
    //   count.value = 2;
    //   // console.log(count.value);
    // }, 3000);

    // function getData() {
    //   return new Promise((resolve, reject) => {
    //     // resolve(100);
    //     reject('error');
    //   })
    // }

    // 当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时，侦听器会被链接到该组件的生命周期，并在组件卸载时自动停止
    // 显式调用返回值，只要执行就会停止侦听
    // const stop = watchEffect(async (onInvalidate) => {
      // 首次组件加载完成后会立即执行一次，之后要是有更新会重新执行
      // 这个函数里面所使用到的数据，都会自动去搜集依赖，监控它的变化
      // 在这个组件卸载的时候会自动销毁
      // console.log(count.value);

      // onInvalidate 是一个函数，接收一个回调函数
      // 除首次外每次都是最先执行的(在监控到数据更新时，onInvalidate会在即将执行的watchEffect回调函数前执行)
      // 哪怕有 stop 也会在那之前执行
      // const data = await getData(); // 为了取消副作用 effect
      // console.log(data);

      // vue3中这个watchEffect 与 react的useEffect 不一样的地方
      // watchEffect 对return 很重视  async 和 await 会隐式的返回一个promise
      // 这个地方就可以判断这个promise 对一些返回的错误进行处理 而react是需要另外写一个函数

      // onInvalidate(() => {
      //   console.log('onInvalidate is trigger');
      // });
    // });

    // 当一个用户定义的副作用函数进入队列时
    // 默认情况下，会在所有的组件 update 前执行
    // onBeforeUpdate(() => {
    //   console.log('onBeforeUpdate');
    // })

    // onMounted(() => {
    //   console.log('onMounted');

    //   watchEffect(() => {
    //     console.log(myRef.value); // 在onMounted 回调里执行就能第一次时拿到
    //   })
    // })

    // watchEffect(() => {
    //   // console.log(myRef.value); // 第一次会拿到null 第二次才会拿到值

    //   console.log(count.value);
      
    // }, {
    //   flush: 'pre',
    //   // flush: 'post' // 在 组件update之后执行
    //   // flush 选项还接受 sync，这将强制效果始终同步触发。
    //   // 然而，这是低效的，应该很少需要

    //   // 这两个选项函数都是只能在开发模式下工作

    //   onTrack(e) { // 将在响应式 property 或 ref 作为依赖项被追踪时被调用
    //     console.log('track', e);
    //     // debugger
    //   },

    //   onTrigger(e) { // 将在依赖项变更导致副作用被触发时被调用
    //     // console.log('trigger', e);
    //     // debugger
    //   }
    // })

    // return {
    //   count,
    //   myRef
    // }

    // --------------------------------------------------

    // watch
    // watch API与选项式API this.$watch(以及相应的 watch 选项)完全等效。
    // watch 需要侦听特定的数据源，并在单独的回调函数中执行副作用
    // 默认情况下，它也是惰性的 —— 即回调仅在侦听源发生更改时被调用。

    // 与 watchEffect 比较
    // 1. 惰性地执行副作用；
    // 2. 更具体地说明应触发侦听器重新运行的状态；
    // 3. 访问被侦听状态的先前值和当前值。


    const count = ref(0),
          name = ref('张三');
    const state = reactive({ a: 1 });

    setTimeout(() => {
      count.value = 1;
      // state.a = 2;
      name.value = '李四';

    }, 2000)

    // watchEffect(() => {
    //   console.log(count.value);
    // })

    // watch(() => {
    //   return count.value;// 侦听单一源
      
    // }, (newVal, oldVal) => {
    //   console.log('is watch', newVal, oldVal);
    // })

    // 简写
    // 使用reactive创建的数据不能直接简写，
    // watch(state.a, (newVal, oldVal) => {
    //   console.log(newVal, oldVal);
    // })

    // watch(() => {
    //   return state.a;
    // }, (newVal, oldVal) => {
    //   console.log(newVal, oldVal);
    // })

    // watch(count.value, (count, prevCount) => console.log('is watch', count, prevCount));

    // 侦听器还可以使用数组同时侦听多个源

    const stop = watch([count.value, name.value],
      ([newCount, newName], [oldCount, oldName],
      onInvalidate // 作为第三个参数传递给回调
    ) => {
      console.log(newCount, newName);
      console.log(oldCount, oldName);

      onInvalidate(() => { // 与 watchEffect 相同的行为
        console.log('onInvalidate is triggered')
      })
    }, {
      flush: 'post',
      onTrack(e) {
        // ....
        // debugger
      },
      onTrigger(e) {
        console.log('trigger', e);
      }
    })

    // stop();

    onBeforeUpdate(() => {
      console.log('onBeforeUpdate')
    })

    return {
      count, name
    }

    // --------------------------------------------------

  }
}
</script>

<style>

</style>
